#include "swoole.h"

typedef struct _swMemoryGlobal_page
{
	struct _swMemoryGlobal_page *next;
	char memory[0];
} swMemoryGlobal_page;

typedef struct _swMemoryGlobal
{
	uint8_t shared;
	uint32_t pagesize;
	swLock lock;
	swMemoryGlobal_page *root_page;
	swMemoryGlobal_page *current_page;
	uint32_t current_offset;
} swMemoryGlobal;

static void *swMemoryGlobal_alloc(swMemoryPool *pool, uint32_t size);
static void swMemoryGlobal_free(swMemoryPool *pool, void *ptr);
static void swMemoryGlobal_destroy(swMemoryPool *poll);
static swMemoryGlobal_page* swMemoryGlobal_new_page(swMemoryGlobal *gm);

swMemoryPool* swMemoryGlobal_new(uint32_t pagesize,uint8_t shared){
	swMemoryGlobal gm, *gm_ptr;
	bzero(&gm,sizeof(swMemoryGlobal));

	gm.shared = shared;
	gm.pagesize = pagesize;

	swMemoryGlobal_page *page = swMemoryGlobal_new_page(&gm);
	if(page == NULL){
		return NULL;
	}

	if(swMutex_create(&gm.lock,shared) <0){
		return NULL;
	}
	
	gm.root_page = page;

	gm_ptr = (swMemoryGlobal *)page->memory;
	gm.current_offset += sizeof(swMemoryGlobal);

	swMemoryPool *allocator = (swMemoryPool *)(page->memory + gm.current_offset);
	gm.current_offset += sizeof(swMemoryPool);

	allocator->object = gm_ptr;
	allocator->alloc = swMemoryGlobal_alloc;
	allocator->destroy = swMemoryGlobal_destroy;
	allocator->free = swMemoryGlobal_free;

	memcpy(gm_ptr,&gm,sizeof(gm));
	return allocator;
}

static swMemoryGlobal_page* swMemoryGlobal_new_page(swMemoryGlobal *gm){
	swMemoryGlobal_page *page = (gm->shared == 1) ? sw_shm_malloc(gm->pagesize) : malloc(gm->pagesize);
	if(page == NULL){
		return NULL;
	}
	bzero(page,gm->pagesize);
	page->next = NULL;

	if(gm->current_page != NULL){
		gm->current_page->next = page;
	}

	gm->current_page = page;
	gm->current_offset = 0;

	return page;
}

static void *swMemoryGlobal_alloc(swMemoryPool *pool, uint32_t size){
	swMemoryGlobal *gm = pool->object;

	gm->lock.lock(&gm->lock);
	if(size > gm->pagesize - sizeof(swMemoryGlobal_page)){
		gm->lock.unlock(&gm->lock);
		return NULL;
	}

	if(size + gm->current_offset > gm->pagesize - sizeof(swMemoryGlobal_page)){
		swMemoryGlobal_page *page = swMemoryGlobal_new_page(gm);
		if(page == NULL){
			gm->lock.unlock(&gm->lock);
			return NULL;
		}
	}

	void *mem = gm->current_page->memory + gm->current_offset;
	gm->current_offset += size;

	gm->lock.unlock(&gm->lock);

	return mem;
}

static void swMemoryGlobal_free(swMemoryPool *pool, void *ptr){
}

static void swMemoryGlobal_destroy(swMemoryPool *poll){
	swMemoryGlobal *gm = poll->object;
	swMemoryGlobal_page *page = gm->root_page;
	swMemoryGlobal_page *next;

	do{
		next = page->next;
		gm->shared == 1 ? sw_shm_free(page) : free(page);
		page = next;
	}while(page);
}
